/*
* CoolQ Demo for VC++
* Api Version 9
* Written by Coxxs & Thanks for the help of orzFly
*/

#include <windows.h>

#include <cstdint>
#include <sstream>

#include <curl/curl.h>
#include <string>
#include <vector>
#include "rapidjson/rapidjson.h"
#include "rapidjson/document.h"
#include "rapidjson/reader.h"
#include "rapidjson/writer.h"

#include "cqp.h"
#include "library.h" //应用AppID等信息，请正确填写，否则酷Q可能无法加载

using namespace rapidjson;
using namespace std;

int ac = -1; //AuthCode 调用酷Q的方法时需要用到
bool enabled = false;

string int2str(int64_t n) {
    std::stringstream ss;
    std::string str;
    ss << n;
    ss >> str;
    return str;
}

size_t write_data(void *ptr, size_t size, size_t nmemb, void *stream) {
    string data((const char *) ptr, (size_t) size * nmemb);
    *((stringstream *) stream) << data << endl;
    return size * nmemb;
}

string buildPostMsg(int32_t type, int32_t subType, int32_t sendTime, int64_t fromGroup,
                    int64_t fromQQ, const char *message, int32_t font) {
    vector<int> xx;
    auto iter = xx.begin();

    Document doc;
    doc.SetObject();
    Document::AllocatorType &allocator = doc.GetAllocator();
    doc.AddMember(StringRef("type"), StringRef(int2str(type).c_str()), allocator);
    doc.AddMember(StringRef("subType"), StringRef(int2str(subType).c_str()), allocator);
    doc.AddMember(StringRef("sendTime"), StringRef(int2str(sendTime).c_str()), allocator);
    doc.AddMember(StringRef("fromGroup"), StringRef(int2str(fromGroup).c_str()), allocator);
    doc.AddMember(StringRef("fromQQ"), StringRef(int2str(fromQQ).c_str()), allocator);
    doc.AddMember(StringRef("message"), StringRef(message), allocator);
    doc.AddMember(StringRef("font"), StringRef(int2str(font).c_str()), allocator);

    StringBuffer buffer;
    //PrettyWriter<StringBuffer> writer(buffer);  //PrettyWriter是格式化的json，如果是Writer则是换行空格压缩后的json
    Writer<StringBuffer> writer(buffer);
    doc.Accept(writer);
    return buffer.GetString();
}

string sendToBackend(const string postmsg) {
    CURL *curl;
    CURLcode res;
    std::stringstream out;
    string str_json;

    curl = curl_easy_init();
    if (curl) {
        curl_easy_setopt(curl, CURLOPT_URL, "http://127.0.0.1:8080/qqMsgInterface");
        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postmsg);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);//设置回调函数
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &out);//设置写数据
        res = curl_easy_perform(curl);
        if (res != CURLE_OK) {
            fprintf(stderr, "curl_easy_perform() failed: %s\n",
                    curl_easy_strerror(res));
        }
        str_json = out.str();//返回请求值
        printf("%s", str_json.c_str());
        curl_easy_cleanup(curl);
    }
    return str_json;
}

void processMsg(int32_t type, int32_t subType, int32_t sendTime, int64_t fromGroup,
                int64_t fromQQ, const char *message, int32_t font) {
    string postJson = buildPostMsg(type, subType, sendTime, fromGroup, fromQQ, message, font);
    CQ_addLog(ac, CQLOG_DEBUG, "postmsg", postJson.c_str());
    string msg;
    switch (type) {
        case PRIVATE_MSG:
            CQ_sendPrivateMsg(ac, fromQQ, message);
            break;
        case GROUP_MSG:
            msg = "[CQ:at,qq=" + int2str(fromQQ) + "]" + string(message);
            CQ_sendGroupMsg(ac, fromGroup, msg.c_str());
            break;
        case DISCUSS_MSG:
            msg = "[CQ:at,qq=" + int2str(fromQQ) + "]" + string(message);
            CQ_sendDiscussMsg(ac, fromGroup, msg.c_str());
            break;
        default:
            break;
    }
}
/*
* 返回应用的ApiVer、Appid，打包后将不会调用
*/
CQEVENT(const char*, AppInfo, 0)() {
    return CQAPPINFO;
}


/*
* 接收应用AuthCode，酷Q读取应用信息后，如果接受该应用，将会调用这个函数并传递AuthCode。
* 不要在本函数处理其他任何代码，以免发生异常情况。如需执行初始化代码请在Startup事件中执行（Type=1001）。
*/
CQEVENT(int32_t, Initialize, 4)(int32_t AuthCode) {
    ac = AuthCode;
    return 0;
}


/*
* Type=1001 酷Q启动
* 无论本应用是否被启用，本函数都会在酷Q启动后执行一次，请在这里执行应用初始化代码。
* 如非必要，不建议在这里加载窗口。（可以添加菜单，让用户手动打开窗口）
*/
CQEVENT(int32_t, _eventStartup, 0)() {
    curl_global_init(CURL_GLOBAL_ALL);
    return 0;
}


/*
* Type=1002 酷Q退出
* 无论本应用是否被启用，本函数都会在酷Q退出前执行一次，请在这里执行插件关闭代码。
* 本函数调用完毕后，酷Q将很快关闭，请不要再通过线程等方式执行其他代码。
*/
CQEVENT(int32_t, _eventExit, 0)() {
    curl_global_cleanup();
    return 0;
}

/*
* Type=1003 应用已被启用
* 当应用被启用后，将收到此事件。
* 如果酷Q载入时应用已被启用，则在_eventStartup(Type=1001,酷Q启动)被调用后，本函数也将被调用一次。
* 如非必要，不建议在这里加载窗口。（可以添加菜单，让用户手动打开窗口）
*/
CQEVENT(int32_t, _eventEnable, 0)() {
    enabled = true;
    return 0;
}


/*
* Type=1004 应用将被停用
* 当应用被停用前，将收到此事件。
* 如果酷Q载入时应用已被停用，则本函数*不会*被调用。
* 无论本应用是否被启用，酷Q关闭前本函数都*不会*被调用。
*/
CQEVENT(int32_t, _eventDisable, 0)() {
    enabled = false;
    return 0;
}


/*
* Type=21 私聊消息
* subType 子类型，11/来自好友 1/来自在线状态 2/来自群 3/来自讨论组
*/
CQEVENT(int32_t, _eventPrivateMsg, 24)(int32_t subType, int32_t sendTime, int64_t fromQQ, const char *msg,
                                       int32_t font) {

    //如果要回复消息，请调用酷Q方法发送，并且这里 return EVENT_BLOCK - 截断本条消息，不再继续处理  注意：应用优先级设置为"最高"(10000)时，不得使用本返回值
    //如果不回复消息，交由之后的应用/过滤器处理，这里 return EVENT_IGNORE - 忽略本条消息
    processMsg(21, subType, sendTime, 0, fromQQ, msg, font);
    return EVENT_BLOCK;
}


/*
* Type=2 群消息
*/
CQEVENT(int32_t, _eventGroupMsg, 36)(int32_t subType, int32_t sendTime, int64_t fromGroup, int64_t fromQQ,
                                     const char *fromAnonymous, const char *msg, int32_t font) {
    processMsg(2, subType, sendTime, fromGroup, fromQQ, msg, font);
    return EVENT_BLOCK; //关于返回值说明, 见“_eventPrivateMsg”函数
}


/*
* Type=4 讨论组消息
*/
CQEVENT(int32_t, _eventDiscussMsg, 32)(int32_t subType, int32_t sendTime, int64_t fromDiscuss, int64_t fromQQ,
                                       const char *msg, int32_t font) {
    processMsg(4, subType, sendTime, fromDiscuss, fromQQ, msg, font);
    return EVENT_BLOCK; //关于返回值说明, 见“_eventPrivateMsg”函数
}


/*
* Type=101 群事件-管理员变动
* subType 子类型，1/被取消管理员 2/被设置管理员
*/
CQEVENT(int32_t, _eventSystem_GroupAdmin, 24)(int32_t subType, int32_t sendTime, int64_t fromGroup,
                                              int64_t beingOperateQQ) {

    return EVENT_IGNORE; //关于返回值说明, 见“_eventPrivateMsg”函数
}


/*
* Type=102 群事件-群成员减少
* subType 子类型，1/群员离开 2/群员被踢 3/自己(即登录号)被踢
* fromQQ 操作者QQ(仅subType为2、3时存在)
* beingOperateQQ 被操作QQ
*/
CQEVENT(int32_t, _eventSystem_GroupMemberDecrease, 32)(int32_t subType, int32_t sendTime, int64_t fromGroup,
                                                       int64_t fromQQ, int64_t beingOperateQQ) {

    return EVENT_IGNORE; //关于返回值说明, 见“_eventPrivateMsg”函数
}


/*
* Type=103 群事件-群成员增加
* subType 子类型，1/管理员已同意 2/管理员邀请
* fromQQ 操作者QQ(即管理员QQ)
* beingOperateQQ 被操作QQ(即加群的QQ)
*/
CQEVENT(int32_t, _eventSystem_GroupMemberIncrease, 32)(int32_t subType, int32_t sendTime, int64_t fromGroup,
                                                       int64_t fromQQ, int64_t beingOperateQQ) {

    return EVENT_IGNORE; //关于返回值说明, 见“_eventPrivateMsg”函数
}


/*
* Type=201 好友事件-好友已添加
*/
CQEVENT(int32_t, _eventFriend_Add, 16)(int32_t subType, int32_t sendTime, int64_t fromQQ) {

    return EVENT_IGNORE; //关于返回值说明, 见“_eventPrivateMsg”函数
}


/*
* Type=301 请求-好友添加
* msg 附言
* responseFlag 反馈标识(处理请求用)
*/
CQEVENT(int32_t, _eventRequest_AddFriend, 24)(int32_t subType, int32_t sendTime, int64_t fromQQ, const char *msg,
                                              const char *responseFlag) {

    //CQ_setFriendAddRequest(ac, responseFlag, REQUEST_ALLOW, "");

    return EVENT_IGNORE; //关于返回值说明, 见“_eventPrivateMsg”函数
}


/*
* Type=302 请求-群添加
* subType 子类型，1/他人申请入群 2/自己(即登录号)受邀入群
* msg 附言
* responseFlag 反馈标识(处理请求用)
*/
CQEVENT(int32_t, _eventRequest_AddGroup, 32)(int32_t subType, int32_t sendTime, int64_t fromGroup, int64_t fromQQ,
                                             const char *msg, const char *responseFlag) {

    //if (subType == 1) {
    //	CQ_setGroupAddRequestV2(ac, responseFlag, REQUEST_GROUPADD, REQUEST_ALLOW, "");
    //} else if (subType == 2) {
    //	CQ_setGroupAddRequestV2(ac, responseFlag, REQUEST_GROUPINVITE, REQUEST_ALLOW, "");
    //}

    return EVENT_IGNORE; //关于返回值说明, 见“_eventPrivateMsg”函数
}

/*
* 菜单，可在 .json 文件中设置菜单数目、函数名
* 如果不使用菜单，请在 .json 及此处删除无用菜单
*/
CQEVENT(int32_t, _menuA, 0)() {
    MessageBox(NULL, "这是menuA，在这里载入窗口，或者进行其他工作。", "title", MB_OK);
    return 0;
}

CQEVENT(int32_t, _menuB, 0)() {
    MessageBox(NULL, "这是menuB，在这里载入窗口，或者进行其他工作。", "title", MB_OK);
    return 0;
}
